#include "strvec.h"

StrVec::StrVec(const StrVec &s)
{
    // 调用 alloc_n_copy 分配空间以容纳与 s 中一样多的元素
    auto newdata = alloc_n_copy(s.begin(), s.end());
    elements = newdata.first;
    first_free = newdata.second;
    cap = newdata.second;
}

StrVec &StrVec::operator =(const StrVec &rhs)
{
    // 调用 alloc_n_copy 分配内存,大小与 rhs 中元素占用空间一样多
    auto data = alloc_n_copy(rhs.begin(), rhs.end());
    free();
    elements = data.first;
    first_free = cap = data.second;
    return *this;
}

StrVec& StrVec::operator =(StrVec &&rhs)
{
    // 直接检测自赋值
    if (this != &rhs) {
        free(); // 释放已有元素
        elements = rhs.elements;    // 从 rhs 接管资源
        first_free = rhs.first_free;
        cap = rhs.cap;
        // 将 rhs 置于可析构状态
        rhs.elements = rhs.first_free = rhs.cap = nullptr;
    }
    return *this;
}

StrVec::StrVec(StrVec &&s) noexcept :
    // 成员初始化器中接管 s 中的资源
    elements(s.elements), first_free(s.first_free), cap(s.cap)
{
    // 令 s 进入这样一种状态：对其进行析构函数是安全的
    s.elements = s.first_free = s.cap = nullptr;
}

StrVec::~StrVec()
{
    free();
}

void StrVec::push_back(const std::string &s)
{
    chk_n_alloc();  // 确保有空间容纳新元素
    // 在 first_free 指向的元素中构造 s 的副本
    alloc.construct(first_free++, s);
}

void StrVec::chk_n_alloc()
{
    if (size() == capacity()) {
        reallocate();
    }
}

std::pair<std::string *, std::string *>
StrVec::alloc_n_copy(const std::string *b, const std::string *e)
{
    // 分配空间保存给定范围中的元素
    auto data = alloc.allocate(e - b);
    // 初始化并返回一个 pair,该 pair 由 data 和 uninitialized_copy 的返回值构成
    return {data, std::uninitialized_copy(b, e, data)};
}

void StrVec::free()
{
    // 不能传递给deallocate 一个空指针,如果 elements 为 0,函数什么也不做
    if (elements) {
        // 逆序销毁旧元素
        for (auto p = first_free; p != elements; /*空*/) {
            alloc.destroy(--p);
        }
        alloc.deallocate(elements, cap - elements);
    }
}

void StrVec::reallocate()
{
    // 我们将分配当前大小两倍的内存空间
    auto newcapacity = size() ? 2 * size() : 1;
    // 分配新内存
    auto newdata = alloc.allocate(newcapacity);
    // 将数据从旧内存移动到新内存
    auto dest = newdata;    // 指向新数组中下一个空闲位置
    auto elem = elements;   // 指向旧数据中下一个元素
    for (size_t i = 0; i != size(); i++) {
        alloc.construct(dest++, std::move(*elem++));
    }
    free(); // 一旦我们移动完元素就释放旧内存空间
    // 更新我们的数据结构，执行新元素
    elements = newdata;
    first_free = dest;
    cap = elements + newcapacity;
}
